在AWS EC2上執行Express專案


需求描述:原本的demo專案是用授課方提供的Heroku平台與MySQL資料庫,後因這兩者皆已到期,所以決定自己上線demo網站.

實作流程:
於AWS EC2 上建立VM
在VM上安裝MySQL資料庫
在VM上安裝Docker相關套件
把GIT上的Express專案建立成container
在VM上安裝NGINX
在VM上安裝Certbot來獲取SSL(HTTPS)

預期架構:

於AWS EC2 上建立VM

註:本篇使用AWS EC2免費方案,但操作過程可能產生額外收費的情形(資料的傳輸等)。

前置作業

  • 已註冊AWS帳號

建立VM

1.登入至AWS,進入EC2控制台,點擊右上登入主控台,搜尋"EC2"點擊第一個
進入https://aws.amazon.com/tw/


2.建立VM

  • 點擊"啟動執行個體"
  • 設置VM名稱
  • 選擇作業系統,本文操作皆使用"Red Hat Enterprise Linux 9"
  • 執行個體類型,可視為該VM的硬體配置(CPU,記憶體),免費方案只有"t2.micro"可選擇,跑個人專案還是夠的
  • 建立金鑰對,為後續連線VM的驗證金鑰,點選"建立新的金鑰對",輸入金鑰對名稱後即可建立,此時會自動下載一個pem檔案,先保留著
  • 網路設定,勾選 "允許來自網際網路的 HTTP(S) 流量",這樣在VM建立後,會自動在安全性群組的輸入規則加入80/tcp,443/tcp這兩條
  • 創建VM,拉至最底下,點擊"啟動執行個體"

登入至VM

我們會採用SSH的方式連線至VM,所以要在你的電腦安裝相關SSH連線程式,本文則是使用"Xshell 7"這套軟體來連線

  • 開啟Xshell 7並使用金鑰對來連線,在AWS頁面左側選取"執行個體"即可看到已建立的VM清單,點擊"執行個體ID"以查看該VM的詳細資訊
  • 點擊"連線",此時應會跳至"SSH 用戶端"這一頁,直接點擊最下面的複製按鈕,此為SSH的連線指令
    註:可看到第三點建議修改金鑰的權限,Windows系統的用戶可安裝"Cygwin",這樣就可在Windows下執行Linux指令了,另注意Windows與Linux檔案路徑的斜線"
    \","/"是相反的

  • 回到Xshell上,直接右鍵貼上剛剛的指令,然後enter,點選"接受及存檔"
    註:在Xshell上預設是無法用ctrl+V的方式,是預防該指令會執行在登入中的系統,所以用右鍵貼上的方式即可
  • 選擇使用者金鑰,點擊"匯入",在下方選"安全性"並進入該VM的安全行群組匯入之前下載的pem檔案,確定即可,之後連線就選該金鑰就可以了


    成功登入

在VM上安裝MySQL資料庫

設定VM的開放port

  • 首先回到該VM的詳細資料頁面,在下方選"安全性"並進入該VM的安全行群組
  • 因此VM只有SQL資料庫運行,所以在創建時的網路設定不需勾選"允許http(s)",在下方的傳入規則就會看到只有一條SSH,接著進入"編輯傳入規則",並"新增規則"
    類型:自訂TCP
    連接埠範圍:3306
    來源:0.0.0.0/0
    然後"儲存規則"

安裝MySQL

登入至VM,開始輸入下列指令

  • 更新
    sudo yum update -y
  • 安裝MySQL
    sudo yum install -y mysql-server
  • 修改設定檔,於最底部加入bind-address=0.0.0.0
    sudo vi /etc/my.cnf.d/mysql-server.cnf
  • 啟動MySQL服務
    sudo systemctl start mysqld.service
  • 設定開機自動啟動
    sudo systemctl enable mysqld.service
  • 查看服務狀態
    sudo systemctl status mysqld.service

    ctrl+C後enter可跳出
  • 查看port狀態
    netstat -anltp|grep :3306

    註:此作業系統應為最小化安裝,許多工具套件是沒有先安裝的,所以接下來遇到"command not found"的部分就需安裝對應的套件
  • 安裝netstat套件
    sudo yum install -y net-tools

建立資料庫使用者

登入至VM,開始輸入下列指令

  • 使用 root 帳號登入到 MySQL
    mysql -u root -p
    會要求輸入password,直接按Enter即可
  • 建立使用者,user01pass1234分別為帳號跟密碼,可設定為自己要用的
    CREATE USER 'user01'@'localhost' IDENTIFIED BY 'pass1234';
    
    CREATE USER 'user01'@'%' IDENTIFIED BY 'pass1234';
    
    GRANT ALL ON *.* TO 'user01'@'localhost';
    
    GRANT ALL ON *.* TO 'user01'@'%';
    
    FLUSH PRIVILEGES;
    

    輸入exitMySQL

使用 MySQL Workbench 進行連線測試

設定資料庫的連線IP(使用彈性IP)

  • 於該VM的資料頁面中,可看到"公有 IPv4 地址"跟"彈性IP地址","彈性IP地址"可當作是靜態IP,當VM重新啟動時,公有IPv4地址可能會變動,但彈性IP地址不會,所以建議使用彈性IP
  • 設置彈性IP,在左側欄位"網路和安全"點擊"彈性IP",點擊"配置彈性IP地址",點擊"配置"

  • 進入該IP的摘要,點擊"與彈性IP地址建立關聯"

  • 在"執行個體"選擇你的VM,會有下拉選單讓你選,選好後就"建立關聯",回到VM資訊就可以看到彈性IP綁訂了,SSH連線的指令也會變為這個IP

使用MySQL Workbench

  • 直接至網站下載該軟體
  • 開啟後點擊"+"來新增連線,
    Connetcion Name:自訂名稱
    Hostname:彈性IP地址
    Username:上面所設定的帳戶名稱
    Password:點擊"Store in Vault"來輸入上面設定的密碼
  • 點擊"Test Connection"來驗證連線,出現"Success"後"OK"


  • 在主畫面進入該SQL連線,在一個新的 Query 頁面輸入以下的 SQL 指令去建立資料庫
    create database twitter_sequelize;
    use twitter_sequelize;
    
  • 按下黃色閃電執行,在下方可看到執行成功

    這樣SQL Server設置好了,接下來就到另一台VM來部署網站

在VM上安裝Docker相關套件

  • 依先前步驟再創建一次VM並綁定彈性IP,然後SSH連線
  • 輸入下列指令來安裝docker
  • 更新
    sudo yum update -y
  • 安裝相關dependencies
    sudo yum install -y yum-utils device-mapper-persistent-data lvm2
  • 添加Docker Repo
    sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
  • 安裝Docker
    sudo yum install -y docker-ce
  • 啟動和設定開機自動執行Docker
    sudo systemctl start docker
    sudo systemctl enable docker
  • 確認安裝成功,使用檢查版本的指令
    sudo docker --version
  • 修改Docker執行權限,之後要執行docker相關指令就不需sudo了
    sudo usermod -aG docker <your_username>
  • 重啟VM以確保所有更動生效
    sudo reboot
  • 登入VM測試指令
    docker -v

把GIT上的Express專案建立成container

安裝git並clone專案至VM

  • 安裝GIT
    sudo yum install -y git
  • 下載專案,這邊使用個人的專案,該專案使用 nodejs/express 開發
    git clone https://github.com/Antarctic-penguin/twitter-api-2020.git
  • 使用 cd 指令進入專案資料夾,使用 ls 查看檔案狀態

創建container

  • 這邊簡述一下流程:
    1.可執行的專案(補齊.env , 設定資料庫連線)
    2.製作成docker image(創建Dockerfile)
    3.從docker image創建container

1.可執行的專案

  • 在專案資料夾的路徑下,補上該有的.env檔
    vi .env
    貼上你所需的資訊
  • 修改資料庫的連線資訊
    帳號,密碼,資料庫名稱 改為上一部分所設定的值,"host"就是資料庫VM的彈性IP地址
    vi config/config.json

2.製作成docker image

  • 創建Dockerfile,一樣在專案路徑下,輸入以下
    vi Dockerfile
>vi Dockerfile
# Use an official Node.js runtime as the base image
FROM node:14

# Set the working directory in the container to /app
WORKDIR /app

# Copy package.json and package-lock.json to the working directory
COPY package*.json ./

# Install any needed packages specified in package.json
RUN npm install

# Bundle app source inside Docker image (by copying from your host to your image filesystem)
COPY . .

# Make port 3000 available to the world outside this container
EXPOSE 3000

# Run the app when the container launches
CMD ["node", "app.js"]


因為此專案是用node 14版本開發的,所以FROM node:14

  • 建立image file
    docker build -t twitter_api .
    -t 後接標籤名稱,協助我們確認該image是什麼,最後面的"."為在當前目錄下的dockerfile
    建立完成
  • 確認image狀態
    docker images

3.從docker image創建container

  • 創建container,3000是專案所使用的port,--name後面接container名稱,最後面則是image的ID,
    docker run -d -p 3000:3000 --name api01 419d112ba624
  • 檢查容器運行狀態,可看到該容器且STATUS為UP,若容器沒有啟動,使用docker ps -a可以看到
    docker ps
  • 檢查專案運行狀態,最後面是接Container ID,可以從上圖的資訊獲得
    docker logs 12985ae4c388
    當時在專案內有設置提示訊息,所以看到成功提示且沒有其他錯誤訊息,就代表專案成功運行

註:用docker-compose建立container:

  • 在專案路徑下,創建docker-compose.yml
    vi docker-compose.yml

>vi docker-compose.yml
version: '3'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - .:/app
    ports:
      - "3000:3000"
  • 建立image
    docker compose -f docker-compose.yml build
  • 從image執行container
    docker compose -f docker-compose.yml up -d
  • 檢查container狀態,沒有成功跑起來,於是檢查LOG,發現是"Cannot find module "

  • 把"Cannot find module "跟"docker compose"拿去google,從這篇大概得知是磁碟區路徑不同的問題,拿去問AI就得到了答案


    這樣安裝的module就可被找到了,在volumes區塊加入- /app/node_modules

  • 刪除原本的volumes
    docker compose down -v
  • 重建image跟container,可看到container成功執行
    docker compose -f docker-compose.yml build
    docker compose -f docker-compose.yml up -d

測試API功能

  • 於安全性群組的輸入規則,新增port 3000
  • 建立資料庫的種子資料,首先登入至container裡
    docker exec -it 12985ae4c388 /bin/bash
  • 使用ls可看到專案的檔案都複製到容器裡了
  • 此時就可以把專案裡的 migration 設定檔同步到資料庫
    npx sequelize db:migrate
  • 於MySQL Workbench可看到資料表都建立了
  • 建立種子資料
    npx sequelize db:seed:all

    輸入exit離開容器
  • 於MySQL Workbench可看到用戶資料都建立了
  • 使用Postman發請求,位址是 http:// 彈性IP地址 + port + 路徑,可看到成功回應

這樣API專案就是成功上線了,但為了跟當初Heroku的效果一樣,所以要把IP地址改成網域名稱,並加入https

在VN上安裝NGINX

1.申請網域
原本是想用google domain,但發現已不提供服務,所以就用google建議的Squarespace,這邊購買滿單純的,一年就是20美含安全防護,不像GoDaddy有一堆加購方案,然後第一年有折扣是12美

  • 購買好之後進入你的網域總覽,進入左側的"DNS"
  • 點擊"ADD RECORD"
  • "Host":@ ,"Type"選擇"A","DATA":彈性IP地址,然後"SAVE"
  • 使用Postman測試,位址是 http:// 網域名稱 + port + 路徑,可看到成功回應

接下來要把port 3000移掉,就需要Nginx的幫忙

2.安裝NGINX

  • 安裝NGINX
    sudo yum install -y nginx
  • 安裝firewalld
    sudo yum install -y firewalld
  • 啟用和設定開機自動啟動firewalld
    sudo systemctl start firewalld
    sudo systemctl enable firewalld
    sudo systemctl status firewalld
  • 開啟http(s) port以供NGINX使用,於防火牆新增規則
    sudo firewall-cmd --permanent --add-port={80/tcp,443/tcp}
    sudo firewall-cmd --reload
    sudo firewall-cmd --list-ports
  • 啟用和設定開機自動啟動Nginx
    sudo systemctl start nginx
    sudo systemctl enable nginx
    sudo systemctl status nginx

註: 後續若有重啟NGINX服務,相關的container也要重啟,不然NGINX的設定會套用不到container上

3.修改NGINX設定檔

  • 修改設定檔
    sudo vi /etc/nginx/nginx.conf
    於綠框處( 在server{...}之後 )加入:
    server_name後替換成你的網域名稱

>加入以下
server {
    server_name blade8128.com;
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}
  • 重啟服務
    sudo systemctl restart nginx
  • 重啟容器
    docker restart 12985ae4c388

4.API測試

  • Postman測試,位址是 http:// 網域名稱 + 路徑

    得到 "502 Bad Gateway",看起來是NGINX的問題,所以我們來查看LOG

  • 查看NGINX LOG,至NGINX的LOG路徑
    cd /var/log/nginx/
    sudo ls -ll

  • 查看"error.log"
    sudo tail error.log

    >2024/03/18 11:02:40 [crit] 4661#4661: *1 connect() to 127.0.0.1:3000 failed (13: Permission denied) while connecting to upstream, client: 111.82.65.173, server: blade8128.com, request: "POST /api/signin HTTP/1.1", upstream: "http://127.0.0.1:3000/api/signin", host: "blade8128.com
    


    直接把"(13: Permission denied) while connecting to upstream, client"拿去google 從stackoverflow得知是SELinux的問題,輸入以下:
    sudo setsebool -P httpd_can_network_connect 1

  • 再次Postman測試,成功

在VM上安裝Certbot來獲取SSL(HTTPS)

本文使用Certbot來獲得免費SSL憑證
1.安裝Certbot

  • 使用"yum install certbot-nginx"無法成功安裝Certbot,要使用這篇的方法來安裝
  • 輸入以下
    sudo python3 -m venv /opt/certbot/
    sudo /opt/certbot/bin/pip install --upgrade pip
    sudo /opt/certbot/bin/pip install certbot certbot-nginx
    sudo ln -s /opt/certbot/bin/certbot /usr/bin/certbot
    執行 Certbot 來設定https server,執行後會有幾個問題要答覆
    sudo certbot --nginx
    問題答覆依序是:
    1.輸入信箱
    2.Y
    3.Y
    4.按enter

可看到認證金鑰的檔案都建好了
sudo cat /etc/nginx/nginx.conf

可看到Certbot幫我們改好了NGINX設定檔SSL部分

  • 重啟服務
    sudo systemctl restart nginx
  • 重啟容器
    docker restart 12985ae4c388

2.Postman測試

  • 位址是 https:// 網域名稱 + 路徑,請求成功

    而原本的http就無法請求了

以上內容若有錯誤,歡迎來信blade8128ch@gmail.com


參考資料

【 Cloud 】於 AWS Ubuntu VM 安裝 MySQL
(20) A Step-by-Step Guide to Installing Docker on RHEL 9 Locally. | LinkedIn
Install certbot with nginx on Amazon Linux 2023 | by Eika Chiu | Medium
AWS EC2 上部署 Docker、Nginx、反向代理設定(https)使用紀錄; Deploy Docker, Nginx with https reverse proxy on AWS EC2 step by step.

#docker #linux #ec2 #nodejs #nginx #Certbot






你可能感興趣的文章

如何在 Windows 安裝 OpenPose 跟使用 Python API 來偵測人體姿態

如何在 Windows 安裝 OpenPose 跟使用 Python API 來偵測人體姿態

How to build CICD with Jenkins as code based on container

How to build CICD with Jenkins as code based on container

7月23日 星期四

7月23日 星期四






留言討論